﻿namespace MSSqlSchemaDoc.Core
{
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Reflection;

    /// <summary>
    /// Contains utilities for automatic object property binding.
    /// </summary>
    public static class PropertyBinder
    {
        /// <summary>
        /// Copies the values from the source object to the destination object for all PUBLIC properties that match in name.
        /// Properties must match exactly in name and case.
        /// The method will enumerate the destination object and look for matching properties in the source object.
        /// </summary>
        /// <param name="source">The source of the data.</param>
        /// <param name="destination">The object to be changed/populated.</param>
        public static void CopyValues(object source, object destination)
        {
            // Get all the public properties of the destination object
            foreach (PropertyInfo d in destination.GetType().GetProperties())
            {
                PropertyInfo s = source.GetType().GetProperty(d.Name);

                // If the property exists, set its value
                if (s != null)
                {
                    d.SetValue(destination, s.GetValue(source, null), null);
                }
            }
        }

        /// <summary>
        /// Copies the matching values from the data reader to the the destination object.
        /// The method will enumerate the properties of the detination object and look for matching column names in the data reader.
        /// </summary>
        /// <param name="reader">The data reader to copy from, it must be at the correct position and will not be advanced to the next row by this method.</param>
        /// <param name="destination">The object to be populated.</param>
        /// <param name="specialMatches">
        /// A list of matches to be made when the property names do not exactly match the result set column names.
        /// The matches must be passed in sets of 2.
        /// The first string is the property name and the second string is the result set column name.
        /// </param>
        public static void CopyValues(IDataRecord reader, object destination, params string[] specialMatches)
        {
            // Check for unexpected values
            if (specialMatches.Length % 2 != 0)
            {
                throw new UnevenElementCountException("The special matches collection must have an even number of elements.");
            }

            // Build the special matches
            IDictionary<string, string> matches = new Dictionary<string, string>();
            for (int i = 0; i < specialMatches.Length; i++)
            {
                matches.Add(specialMatches[i], specialMatches[++i]);
            }

            // Get the properties of the destination object
            foreach (PropertyInfo prop in destination.GetType().GetProperties())
            {
                string columnName;

                // Check whether this property name should match a column of a different type
                if (matches.ContainsKey(prop.Name))
                {
                    columnName = matches[prop.Name];
                }
                else
                {
                    columnName = prop.Name;
                }

                int dataPosition = reader.GetOrdinal(columnName);
                prop.SetValue(destination, reader.GetValue(dataPosition), null);
            }
        }
    }
}
